

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

/**
 * 读excel
 * @author andus.top
 * @date 2021/6/1  20:41
*/
public class ExcelReader {

    private Workbook workbook;
    private Map<String, List<List<Object>>> workbookContent;
    private FileInputStream in;

    public ExcelReader(File file) throws IOException {
        String fileName = file.getName();
        in = new FileInputStream(file);
        try{
            if(fileName.endsWith(".xls")){
                workbook = new HSSFWorkbook(in);
            }else if(fileName.endsWith(".xlsx")){
                workbook = new XSSFWorkbook(in);
            }
        }finally {

        }
        if(workbook == null){
            throw new RuntimeException("excel 文件格式错误");
        }
    }

    public ExcelReader(String filePath) throws IOException {
        this(new File(filePath));
    }

    public ExcelReader(FileInputStream in, boolean xls) throws IOException {
        this.in = in;
        if(xls){
            workbook = new HSSFWorkbook(this.in);
        }else{
            workbook = new XSSFWorkbook(this.in);
        }
    }

    /**
     * 获取sheet内容，按指定行作为表头，转存为List&lt;T&gt; 结构
     * @param sheetIndex sheet下标，从0开始
     * @param keyIndex 第几行用作key，从0开始。注意，key行的长度小于内容行，多余的内容会丢失；反之会放入null
     * @param keyIndex sup
     * @author andus.top
     * @date 2021/6/1 19:17
     * @return 将sheet内容从list，转为按指定行做key的map
     */
    public <T> List<T>  sheetList2Bean(int sheetIndex, int keyIndex, Supplier<T> sup) throws IllegalAccessException {
        return sheetList2Bean(getSheetContent(sheetIndex), keyIndex, sup);
    }

    /**
     * 获取sheet内容，按指定行作为表头，转存为List&lt;T&gt; 结构
     * @param sheetName sheet名称
     * @param keyIndex 第几行用作key，从0开始。注意，key行的长度小于内容行，多余的内容会丢失；反之会放入null
     * @param keyIndex sup
     * @author andus.top
     * @date 2021/6/1 19:17
     * @return 将sheet内容从list，转为按指定行做key的map
     */
    public <T> List<T>  sheetList2Bean(String sheetName, int keyIndex, Supplier<T> sup) throws IllegalAccessException {
        return sheetList2Bean(getSheetContent(sheetName), keyIndex, sup);
    }

    /**
     * 获取sheet内容，按指定行作为表头，转存为map结构
     * @param sheetIndex sheet下标，从0开始
     * @param keyIndex 第几行用作key，从0开始。注意，key行的长度小于内容行，多余的内容会丢失；反之会放入null
     * @author andus.top
     * @date 2021/6/1 19:17
     * @return 将sheet内容从list，转为按指定行做key的map
     */
    public List<Map<String, Object>> sheetList2Map(int sheetIndex, int keyIndex){
        return sheetList2Map(getSheetContent(sheetIndex), keyIndex);
    }

    /**
     * 获取sheet内容，按指定行作为表头，转存为map结构
     * @param sheetName sheet名称
     * @param keyIndex 第几行用作key，从0开始。注意，key行的长度小于内容行，多余的内容会丢失；反之会放入null
     * @author andus.top
     * @date 2021/6/1 19:17
     * @return 将sheet内容从list，转为按指定行做key的map
     */
    public List<Map<String, Object>> sheetList2Map(String sheetName, int keyIndex){
        return sheetList2Map(getSheetContent(sheetName), keyIndex);
    }

    /**
     * 获取sheet所有或指定位置的内容。若为合并单元格，子单元格会的值为合并单元格的值<br>
     * note：若在该方法之前已读取了excel数据，那么需调用clearCache方法，清空之前的数据，否则指定范围读取无效
     * @param sheetIndex sheet下标，从0开始
     * @param range  下标从0开始。<b>要么不传，要么4个值都传。</b>-1表示取到最后一行或最后一列<br>
     *  &nbsp; range结构为：[int startRow, int endRow, int startCol, int endCol] <br>
     *  &emsp;&emsp;  startRow 开始的行，从0开始<br>
     *  &emsp;&emsp;  endRow 结束的行<br>
     *  &emsp;&emsp;  startCol 开始的列，从0开始<br>
     *  &emsp;&emsp;  endCol 结束的列<br>
     * @author andus.top
     * @date 2021/6/1 20:46
     */
    public List<List<Object>> getSheetContent(int sheetIndex, Integer...range){
        return getWorkbookContentList(range).get(sheetIndex);
    }

    /**
     * 获取sheet所有或指定位置的内容。若为合并单元格，子单元格会的值为合并单元格的值<br>
     * note：若在该方法之前已读取了excel数据，那么需调用clearCache方法，清空之前的数据，否则指定范围读取无效
     * @param sheetName sheet名称
     * @param range  下标从0开始。<b>要么不传，要么4个值都传。</b>-1表示取到最后一行或最后一列<br>
     *  &nbsp; range结构为：[int startRow, int endRow, int startCol, int endCol] <br>
     *  &emsp;&emsp;  startRow 开始的行，从0开始<br>
     *  &emsp;&emsp;  endRow 结束的行<br>
     *  &emsp;&emsp;  startCol 开始的列，从0开始<br>
     *  &emsp;&emsp;  endCol 结束的列<br>
     * @author andus.top
     * @date 2021/6/1 20:46
     */
    public List<List<Object>> getSheetContent(String sheetName, Integer...range){
        return getWorkbookContentMap(range).get(sheetName);
    }

    /**
     * 遍历excel，获取sheet所有或指定范围的内容。返回List集合<br>
     * note：若在该方法之前已读取了excel数据，那么需调用clearCache方法，清空之前的数据，否则指定范围读取无效
     * @param range  下标从0开始。<b>要么不传，要么4个值都传。</b>-1表示取到最后一行或最后一列<br>
     *  &nbsp; range结构为：[int startRow, int endRow, int startCol, int endCol] <br>
     *  &emsp;&emsp;  startRow 开始的行，从0开始<br>
     *  &emsp;&emsp;  endRow 结束的行<br>
     *  &emsp;&emsp;  startCol 开始的列，从0开始<br>
     *  &emsp;&emsp;  endCol 结束的列<br>
     * @author andus.top
     * @date 2021/6/1 20:41
     * @return 按照sheetIndex->row->cell 逐层获取。List&lt;List&lt;Object&gt;&gt; 为每个sheet数据；List&lt;Object&gt; 为每行数据
    */
    public List<List<List<Object>>> getWorkbookContentList(Integer...range){
        if(workbookContent == null || workbookContent.isEmpty()){
            getWorkbookContentMap(range);
        }
        List<List<List<Object>>> rlist = new ArrayList<>(workbookContent.size());
        workbookContent.forEach((k,v) -> rlist.add(v));
        return rlist;
    }

    /**
     * 遍历excel，获取sheet所有或指定范围的内容。返回Map集合<br>
     * note：若在该方法之前已读取了excel数据，那么需调用clearCache方法，清空之前的数据，否则指定范围读取无效
     * @param range  下标从0开始。<b>要么不传，要么4个值都传。</b>-1表示取到最后一行或最后一列<br>
     *  &nbsp; range结构为：[int startRow, int endRow, int startCol, int endCol] <br>
     *  &emsp;&emsp;  startRow 开始的行，从0开始<br>
     *  &emsp;&emsp;  endRow 结束的行<br>
     *  &emsp;&emsp;  startCol 开始的列，从0开始<br>
     *  &emsp;&emsp;  endCol 结束的列<br>
     * @author andus.top
     * @date 2021/6/1 20:41
     * @return 按照sheetName->row->cell 逐层获取。key为sheet名称；vlaue为每个sheet数据；List&lt;Object&gt; 为每行数据
     */
    public Map<String, List<List<Object>>> getWorkbookContentMap(Integer...range){
        if(range == null || range.length%4 != 0){
            System.err.println("sheet range format is error");
            return null;
        }
        if(workbookContent == null || workbookContent.isEmpty()){
            workbookContent = new HashMap<>();
            int numberOfSheets = workbook.getNumberOfSheets();
            for (int x = 0; x < numberOfSheets; x++) {
                Sheet sheet = workbook.getSheetAt(x);
                workbookContent.put(sheet.getSheetName(), getSheetContent(sheet, range));
            }
        }
        return workbookContent;
    }

    /**
     * 清空workbookContent。<br>
     * 需要指定范围读取数据时，需清空之前的数据
     * @author andus.top
     * @date 2021/6/29 14:24
    */
    public void clearCache(){
        if(workbookContent != null)workbookContent.clear();
    }

    /**
     * 关闭流
     * @author andus.top
     * @date 2021/6/2 20:26
    */
    public void close(){
        try {
            if(workbook != null){
                workbook.close();
            }
            if(in != null){
                in.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取每个sheet的数据。合并单元格，获取第一个cell的值
     * @param sheet 当前org.apache.poi.ss.usermodel.Sheet对象
     * @param range  下标从0开始。<b>要么不传，要么4个值都传。</b>-1表示取到最后一行或最后一列<br>
     *  &nbsp; range结构为：[int startRow, int endRow, int startCol, int endCol] <br>
     *  &emsp;&emsp;  startRow 开始的行，从0开始<br>
     *  &emsp;&emsp;  endRow 结束的行<br>
     *  &emsp;&emsp;  startCol 开始的列，从0开始<br>
     *  &emsp;&emsp;  endCol 结束的列<br>
     * @author andus.top
     * @date 2021/6/1 20:50
    */
    private List<List<Object>> getSheetContent(Sheet sheet, Integer...range){
        if(sheet == null){
            System.err.println("sheet is null");
            return null;
        }

        // // fixme 【已修复】开始行设置为0，保证通过下标获取数据时，不会出错。
        Integer startRow = 0; // sheet.getFirstRowNum()：第0行无数据，第1行有数据，此时返回1
        Integer endRow = sheet.getLastRowNum(); // n行返回n-1
        if(range != null && range.length == 4){
            // 开始行和结束行小于0，或者结束行小于开始行 ，或者其他参数错误时，使用getFirstRowNum或
            if(range[0] > 0 && range[0] <= endRow && (range[0] <= range[1] || range[1] < 0)){ // range[1] < 0 与 getLastRowNum 等价
                startRow = range[0];
            }
            if(range[1] > 0 && range[1] <= endRow  && range[0] <= range[1]){
                endRow = range[1];
            }
        }
        List<List<Object>> slist = new ArrayList<>((endRow - startRow) + 1);
        // 遍历指定范围的行
        for (int x = startRow; x <= endRow; x++){
            slist.add(getRow(sheet.getRow(x), range));
        }
        return slist;
    }
    /**
     * 获取当前row的数据。合并单元格，获取第一个cell的值
     * @param row 当前org.apache.poi.ss.usermodel.Row对象
     * @param range  下标从0开始。<b>要么不传，要么4个值都传。</b>-1表示取到最后一行或最后一列<br>
     *  &nbsp; range结构为：[int startRow, int endRow, int startCol, int endCol] <br>
     *  &emsp;&emsp;  startRow 开始的行，从0开始<br>
     *  &emsp;&emsp;  endRow 结束的行<br>
     *  &emsp;&emsp;  startCol 开始的列，从0开始<br>
     *  &emsp;&emsp;  endCol 结束的列<br>
     * @author andus.top
     * @date 2021/6/1 20:51
    */
    private List<Object> getRow(Row row, Integer...range){
        if(row == null){
            return null;
        }
        // fixme  【已修复】开始行设置为0，保证每列数据不会错误。即，保证转换为key，value是 不会错位
        short startCol = 0; // row.getFirstCellNum()：第0列无数据，第1列有数据，此时返回1
        short endCol = (short)(row.getLastCellNum()-(short) 1); //  n列，返回n。fixme： 【已修复】所以最大列数需减一
        if(range != null && range.length == 4){
            // 开始行和结束行小于0，或者结束行小于开始行 ，或者其他参数错误时，使用getFirstRowNum或
            if(range[2] > 0 && range[2] <= endCol && (range[2] <= range[3] || range[3] < 0)){ // range[3] < 0 与 getLastCellNum 等价
                startCol = Short.valueOf(String.valueOf(range[2]));
            }
            if(range[3] > 0 && range[3] <= endCol  && range[2] <= range[3]){
                endCol = Short.valueOf(String.valueOf(range[3]));
            }
        }

        List<Object> rlist = new ArrayList<>();
        // 遍历指定范围的列
        for (int x = startCol; x <= endCol; x++) {
            Cell cell = row.getCell(x);

            if(cell != null) {
                boolean isMerge = isMergedRegion(row.getSheet(), row.getRowNum(), cell.getColumnIndex());
                //判断是否具有合并单元格
                if (isMerge) {
                    rlist.add(getMergedRegionValue(row.getSheet(), row.getRowNum(), cell.getColumnIndex()));
                } else {
                    rlist.add(getCellValue(cell));
                }
            }else{
                // 添加null
                rlist.add(getCellValue(cell));
            }
        }

        // 直接迭代，读取所有行
        // List<Object> rlist = new ArrayList<>();
        // row.cellIterator().forEachRemaining(cell -> rlist.add(getCell(cell)));
        return rlist;
    }
    /**
     * 获取当前cell的数据
     * @param cell 当前org.apache.poi.ss.usermodel.Cell对象
     * @author andus.top
     * @date 2021/6/1 20:52
     */
    private Object getCellValue(Cell cell){
        if(cell == null){
            return null;
        }else{
            CellType cft = cell.getCellType();
            switch (cft){
                case BLANK:
                    return "";
                case BOOLEAN:
                    return cell.getBooleanCellValue();
                case ERROR:
                    return cell.getErrorCellValue();
                case NUMERIC:
                    if(DateUtil.isCellDateFormatted(cell)){
                       return cell.getLocalDateTimeCellValue();
                    }else{
                        return cell.getNumericCellValue();
                    }
                case FORMULA:
                    return cell.getCellFormula();
                    //return cell.getStringCellValue();
                default:
                    return cell.getStringCellValue();
                    //return cell.getRichStringCellValue().toString();
            }
        }
    }

    /**
     * 判断指定的单元格是否是合并单元格
     * @param sheet
     * @param row 行下标
     * @param column 列下标
     * @return true:是合并单元格，false：非合并单元格
     */
    private static boolean isMergedRegion(Sheet sheet, int row ,int column) {

        // 得到一个sheet中有多少个合并单元格
        int sheetMergeCount = sheet.getNumMergedRegions();
        for (int i = 0; i < sheetMergeCount; i++) {

            // 得出具体的合并单元格
            CellRangeAddress range = sheet.getMergedRegion(i);

            // 得到合并单元格的起始行, 结束行, 起始列, 结束列
            int firstColumn = range.getFirstColumn();
            int lastColumn = range.getLastColumn();
            int firstRow = range.getFirstRow();
            int lastRow = range.getLastRow();

            // 判断该单元格是否在合并单元格范围之内, 如果是, 则返回 true
            if(row >= firstRow && row <= lastRow){
                if(column >= firstColumn && column <= lastColumn){
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 获取合并单元格的值<br>
     * 即获取合并单元格第一个cell的值
     * @param sheet 当前sheet
     * @param row 当前行下标
     * @param column 当前列下标
     * @return 该合并单元格的值
     */
    private Object getMergedRegionValue(Sheet sheet ,int row , int column){

        // 获得一个 sheet 中合并单元格的数量
        int sheetMergeCount = sheet.getNumMergedRegions();

        // 遍历合并单元格
        for(int i = 0 ; i < sheetMergeCount ; i++){

            // 得出具体的合并单元格
            CellRangeAddress ca = sheet.getMergedRegion(i);

            // 得到合并单元格的起始行, 结束行, 起始列, 结束列
            int firstColumn = ca.getFirstColumn();
            int lastColumn = ca.getLastColumn();
            int firstRow = ca.getFirstRow();
            int lastRow = ca.getLastRow();

            // 获取合并单元格第一个cell的值
            if(row >= firstRow && row <= lastRow){
                if(column >= firstColumn && column <= lastColumn){
                    Row fRow = sheet.getRow(firstRow);
                    Cell fCell = fRow.getCell(firstColumn);
                    return getCellValue(fCell) ;
                }
            }
        }

        return null ;
    }

    private <T> List<T>  sheetList2Bean(List<List<Object>> sheet , int keyIndex, Supplier<T> sup) throws IllegalAccessException {
        int s = sheet.size(); // 当前sheet 行数
        List<T> elist = new ArrayList<>();
        List<Object> keyList = sheet.get(keyIndex);
        int ks = keyList.size();
        for (int x = keyIndex + 1; x < s; x++) {
            List<Object> row = sheet.get(x); // 某行数据
            Map<String, Object> emap = new HashMap<>();
            for (int y = 0; y < ks; y++) {
                emap.put(String.valueOf(keyList.get(y)), tryGet(row, y));
            }
            elist.add(map2Bean(emap, sup));
        }
        return elist;
    }

    private List<Map<String, Object>> sheetList2Map(List<List<Object>> sheet , int keyIndex){
        int rowNum = sheet.size();
        // 将每行map的对象存入List
        List<Map<String, Object>> elementList = new ArrayList<>();
        // 暂存指定的key
        List<Object> keyList = sheet.get(keyIndex);
        int keyNum = keyList.size();
        for (int x = keyIndex + 1; x < rowNum; x++) {
            List<Object> row = sheet.get(x);
            Map<String, Object> emap = new HashMap<>();
            elementList.add(emap);
            for (int y = 0; y < keyNum; y++) {
                emap.put(String.valueOf(keyList.get(y)), tryGet(row, y));
            }
        }
        return elementList;
    }

    // 可放于其他工具类
    private <T> T map2Bean(Map<String, Object> map, Supplier<T> sup) throws IllegalAccessException {
        T t = sup.get();
        Field[] fields = t.getClass().getDeclaredFields();
        for (Field field : fields) {
            String fname = field.getName();
            if(!Modifier.isStatic(field.getModifiers())){// 只取非静态对象
                field.setAccessible(true);
                Object o = map.get(fname);
                Class<?> type = field.getType();
                try {
                    if (o != null) {
                        switch (type.getName()) {
                            case "java.lang.String":
                                field.set(t, o.toString());
                                break;
                            case "int":
                                field.set(t, IntegerKit.parseInt(o));
                                break;
                            case "java.lang.Integer":
                                field.set(t, IntegerKit.of(o));
                                break;
                            case "java.time.LocalDateTime":
                                if (o instanceof LocalDateTime) {
                                    field.set(t, o);
                                } else {
                                    field.set(t, LocalDateTime.parse(o.toString(), DateTimeFormatter.ofPattern("yyyy-M-d  HH:mm:ss")));
                                }
                                break;
                            case "double":
                                field.set(t, DoubleKit.parseDouble(o));
                                break;
                            case "java.lang.Double":
                                field.set(t, DoubleKit.of(o));
                                break;
                            case "boolean":
                                field.set(t, BooleanKit.parseBoolean(o));
                                break;
                            case "java.lang.Boolean":
                                field.set(t, BooleanKit.of(o));
                                break;
                            case "float":
                                field.set(t, FloatKit.parseFloat(o));
                                break;
                            case "java.lang.Float":
                                field.set(t, FloatKit.of(o));
                                break;
                            case "short":
                                field.set(t, ShortKit.parseShort(o));
                                break;
                            case "java.lang.Short":
                                field.set(t, ShortKit.of(o));
                                break;
                            case "byte":
                                field.set(t, ByteKit.parseByte(o));
                                break;
                            case "java.lang.Byte":
                                field.set(t, ByteKit.of(o));
                                break;
                            default:
                                break;

                        }
                    } else {
                        System.err.println("data is null");
                    }
                }catch (Exception e){
                    e.printStackTrace();
                    // 打印数据转换错误
                    if (o != null)System.err.println("--->>>>field is " + field.getName() + ", field type is " + type + ",value is " + o + ", value type is " + o.getClass());
                }
            }
        }
        return t;
    }

    /**
     * 引用类型转换为基本类型
     * https://blog.csdn.net/csm_qz/article/details/46853567
     * @param cls
     * @author andus.top
     * @date 2021/6/1 16:08
     */
    private static Class<?> getBasicClass(Class<?> cls){
        if (Integer.class.equals(cls)) {
            return Integer.TYPE; //return int.class;
        }else if(Long.class.equals(cls)){
            return Long.TYPE; // return long.class;
        }else if(Float.class.equals(cls)){
            return Float.TYPE; // return float.class;
        }else if(Boolean.class.equals(cls)){
            return Boolean.TYPE;// return boolean.class;
        }else if(Character.class.equals(cls)){
            return Character.TYPE; // return char.class;
        }else if(Byte.class.equals(cls)){
            return Byte.TYPE; // return byte.class
        }else if(Void.class.equals(cls)){
            return Void.TYPE; // return void.class
        }else if(Short.TYPE.equals(cls)){
            return Short.TYPE; // short.class
        }
        return cls;
    }

    // 可放于其他工具类
    private <T>T tryGet(List<T> list, int index){
        if(list == null || list.size() <= index){
            return null;
        }
        return list.get(index);
    }

    static class IntegerKit {
        public static Integer of(Object o){
            if(o == null){
                return null;
            }
            if(o instanceof Integer){
                return (Integer)o;
            }
            if(o instanceof Number){
                return ((Number)o).intValue();
            }
            return of(o.toString());
        }
        public static Integer of(String s){
            if(s == null || s.isEmpty()){
                return null;
            }
            return Integer.valueOf(s);
        }

        public static int parseInt(String s){
            if(s == null || s.isEmpty()){
                return 0;
            }
            return Integer.parseInt(s);
        }

        public static int parseInt(Object o){
            Integer i = of(o);
            if(i == null){
                return 0;
            }
            return i;
        }
    }

    static class FloatKit {
        public static Float of(Object o){
            if(o == null){
                return null;
            }
            if(o instanceof Float){
                return (Float)o;
            }
            if(o instanceof Number){
                return ((Number)o).floatValue();
            }
            return of(o.toString());
        }
        public static Float of(String s){
            if(s == null || s.isEmpty()){
                return null;
            }
            return Float.valueOf(s);
        }

        public static float parseFloat(String s){
            if(s == null || s.isEmpty()){
                return 0l;
            }
            return Float.parseFloat(s);
        }

        public static float parseFloat(Object o){
            Float f = of(o);
            if(f == null){
                return 0l;
            }
            return f;
        }
    }

    static class DoubleKit {
        public static Double of(Object o){
            if(o == null){
                return null;
            }
            if(o instanceof Double){
                return (Double)o;
            }
            if(o instanceof Number){
                return ((Number)o).doubleValue();
            }
            return of(o.toString());
        }
        public static Double of(String s){
            if(s == null || s.isEmpty()){
                return null;
            }
            return Double.valueOf(s);
        }

        public static double parseDouble(String s){
            if(s == null || s.isEmpty()){
                return 0;
            }
            return Double.parseDouble(s);
        }

        public static double parseDouble(Object o){
            Double d = of(o);
            if(d == null){
                return 0;
            }
            return d;
        }
    }

    static class BooleanKit {
        public static Boolean of(Object o){
            if(o == null){
                return null;
            }
            if(o instanceof Boolean){
                return (Boolean)o;
            }
            return of(o.toString());
        }
        public static Boolean of(String s){
            if(s == null || s.isEmpty()){
                return null;
            }
            return Boolean.valueOf(s);
        }

        public static boolean parseBoolean(String s){
            if(s == null || s.isEmpty()){
                return false;
            }
            return Boolean.valueOf(s);
        }

        public static boolean parseBoolean(Object o){
            Boolean b = of(o);
            if(b == null){
                return false;
            }
            return b;
        }
    }

    static class ByteKit {
        public static Byte of(Object o){
            if(o == null){
                return null;
            }
            if(o instanceof Byte){
                return (Byte)o;
            }
            return of(o.toString());
        }
        public static Byte of(String s){
            if(s == null || s.isEmpty()){
                return null;
            }
            return Byte.valueOf(s);
        }

        public static byte parseByte(String s){
            if(s == null || s.isEmpty()){
                return 0;
            }
            return Byte.parseByte(s);
        }

        public static byte parseByte(Object o){
            Byte b = of(o);
            if(b == null){
                return 0;
            }
            return b;
        }
    }

    static class ShortKit {
        public static Short of(Object o){
            if(o == null){
                return null;
            }
            if(o instanceof Short){
                return (Short)o;
            }
            if(o instanceof Short){
                return ((Short)o).shortValue();
            }
            return of(o.toString());
        }
        public static Short of(String s){
            if(s == null || s.isEmpty()){
                return null;
            }
            return Short.valueOf(s);
        }

        public static short parseShort(String s){
            if(s == null || s.isEmpty()){
                return 0;
            }
            return Short.parseShort(s);
        }

        public static short parseShort(Object o){
            Short s = of(o);
            if(s == null){
                return 0;
            }
            return s;
        }
    }

}
